﻿using Coder.Configs;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using Egg;
using System.Xml.Linq;

namespace Coder
{
    public static class ModuleMaker
    {
        /// <summary>
        /// 调试模式
        /// </summary>
        public static bool DebugMode = false;

        /// <summary>
        /// 输出对象
        /// </summary>
        public static IOutput? Output = null;

        // 获取内容
        static string GetContent(ModuleData obj, string content)
        {
            StringBuilder sb = new StringBuilder();
            StringBuilder key = new StringBuilder();
            for (int i = 0; i < content.Length; i++)
            {
                char chr = content[i];
                switch (chr)
                {
                    case '$':
                        if (key.Length <= 0)
                        {
                            key.Append(chr);
                        }
                        else if (key.Length == 1 && key[0] == '$')
                        {
                            sb.Append(chr);
                            key.Clear();
                        }
                        else
                        {
                            throw new Exception($"意外的'{chr}'字符");
                        }
                        break;
                    case '@':
                        if (key.Length <= 0)
                        {
                            key.Append(chr);
                        }
                        else if (key.Length == 1 && key[0] == '@')
                        {
                            sb.Append(chr);
                            key.Clear();
                        }
                        else
                        {
                            throw new Exception($"意外的'{chr}'字符");
                        }
                        break;
                    case '"':
                        // 兼容C#代码
                        if (key.Length == 1)
                        {
                            if (key[0] != '$') throw new Exception($"意外的'{chr}'字符");
                            sb.Append('$');
                            sb.Append(chr);
                            key.Clear();
                        }
                        else if (key.Length > 0)
                        {
                            key.Append(chr);
                        }
                        else
                        {
                            sb.Append(chr);
                        }
                        break;
                    case '(':
                        if (key.Length >= 1)
                        {
                            key.Append(chr);
                        }
                        else
                        {
                            sb.Append(chr);
                        }
                        break;
                    case ')':
                        if (key.Length > 0)
                        {
                            //处理关键字
                            if (key.Length < 2) throw new Exception($"意外的'{chr}'字符");
                            if (key[0] != '$' && key[0] != '@') throw new Exception($"意外的'{chr}'字符");
                            if (key[1] != '(') throw new Exception($"意外的'{chr}'字符");
                            string[] keys = key.ToString(2, key.Length - 2).Split('.');
                            if (keys.Length != 2) throw new Exception($"不支持的变量'{key.ToString(2, key.Length - 2)}'");
                            if (keys[0] == "System")
                            {
                                sb.Append(SystemOpreator.GetContent(keys[1]));
                                key.Clear();
                            }
                            else if (keys[0] == "Guid")
                            {
                                sb.Append(GuidOpreator.GetContent(keys[1]));
                                key.Clear();
                            }
                            else
                            {
                                if (!obj.ContainsKey(keys[0])) throw new Exception($"对象'{keys[0]}'尚未定义");
                                var objParent = obj[keys[0]];
                                if (!objParent.ContainsKey(keys[1])) throw new Exception($"变量'{keys[0]}.{keys[1]}'尚未定义");
                                sb.Append(objParent[keys[1]]);
                                key.Clear();
                            }
                        }
                        else
                        {
                            sb.Append(chr);
                        }
                        break;
                    default:
                        if (key.Length == 1)
                        {
                            sb.Append(key.ToString());
                            key.Clear();
                        }
                        if (key.Length > 0)
                        {
                            key.Append(chr);
                        }
                        else
                        {
                            sb.Append(chr);
                        }
                        break;
                }
            }
            return sb.ToString();
        }

        // 复制文件
        static void CopyFile(ModuleData obj, string sourcePath, string targetPath)
        {
            //string content = GetContent(obj, egg.IO.ReadUtf8FileContent(sourcePath));
            //egg.IO.WriteUtf8FileContent(targetPath, content);
            try
            {
                string content = GetContent(obj, egg.IO.ReadUtf8FileContent(sourcePath));
                egg.IO.WriteUtf8FileContent(targetPath, content);
            }
            catch (Exception ex)
            {
                throw new Exception($"复制文件错误\n路径: {sourcePath}\n错误: {ex.Message}", ex.InnerException);
            }
        }

        // 复制目录
        static void MakeCodeFromConfig(CoderConfig coder, ModuleConfig config, string sourcePath, string targetPath)
        {
            var obj = config.Data;
            egg.IO.CreateFolder(targetPath);
            // 创建所有的文件夹
            for (int i = 0; i < config.IO.Folders.Count; i++)
            {
                string dirPath = GetContent(obj, config.IO.Folders[i]);
                string fullDirPath = System.IO.Path.Combine(targetPath, dirPath);
                Output?.WriteLine($"[Create] {fullDirPath}");
                egg.IO.CreateFolder(fullDirPath);
            }
            // 复制所有的文件
            for (int i = 0; i < config.IO.Files.Count; i++)
            {
                var file = config.IO.Files[i];
                string filePath = GetContent(obj, file.TargetPath);
                // 判断模块是否工作
                if (!config.Availabilities.GetValue<bool>(file.ModuleName))
                {
                    Output?.WriteLine($"[Skip] {filePath} : {file.ModuleName} module not availabilitiy.");
                    continue;
                }
                string fullFilePath = System.IO.Path.Combine(targetPath, filePath);
                if (egg.IO.CheckFileExists(fullFilePath))
                {
                    if (coder.Overwrite)
                    {
                        Output?.WriteLine($"[Cover] {filePath} -> {fullFilePath}");
                        CopyFile(obj, System.IO.Path.Combine(sourcePath, file.SourcePath), fullFilePath);
                    }
                    else
                    {
                        Output?.WriteLine($"[Skip] {filePath}");
                    }
                }
                else
                {
                    Output?.WriteLine($"[Copy] {filePath} -> {fullFilePath}");
                    CopyFile(obj, System.IO.Path.Combine(sourcePath, file.SourcePath), fullFilePath);
                }
            }
        }

        /// <summary>
        /// 从配置文件生成代码
        /// </summary>
        /// <param name="coder"></param>
        /// <param name="pathSetting"></param>
        /// <param name="sourcePath"></param>
        /// <param name="targetPath"></param>
        public static void MakeCodeFromPath(CoderConfig coder, string workPath, string settingPath, string sourcePath, string targetPath)
        {
            if (DebugMode) Output?.WriteLine($"[Debug] 加载配置文件'{settingPath}' ...");

            // 生成默认配置文件
            if (!egg.IO.CheckFileExists(settingPath))
            {
                // 获取文件夹
                string folder = System.IO.Path.GetDirectoryName(settingPath) ?? "";
                egg.IO.CreateFolder(folder);

                // 创建一个新的配置文件
                var module = ModuleConfig.CreateProjectModule();

                // 保存文件
                var content = JsonSerializer.Serialize(module);
                egg.IO.WriteUtf8FileContent(settingPath, content);
            }

            // 读取配置
            {
                var content = egg.IO.ReadUtf8FileContent(settingPath);
                var module = JsonSerializer.Deserialize<ModuleConfig>(content);
                if (module is null) { Output?.WriteLine($"[Error] 配置文件'{settingPath}'解析失败"); return; }

                // 处理输出目录
                if (string.IsNullOrEmpty(targetPath)) targetPath = SystemOpreator.GetClosePath(System.Environment.CurrentDirectory);

                sourcePath = System.IO.Path.Combine(sourcePath, module.IO.SourcePath);
                targetPath = System.IO.Path.Combine(targetPath, module.IO.TargetPath);

                if (!module.Enable) { Output?.WriteLine($"[Skip] 配置文件'{settingPath}'状态不可用"); return; }
                if (module.IO.Files.Count <= 0) { Output?.WriteLine($"[Skip] 配置文件'{settingPath}'缺少输出文件定义"); return; }

                // 设置系统变量
                SystemOpreator.Set("WorkPath", workPath);
                SystemOpreator.Set("SourcePath", sourcePath);
                SystemOpreator.Set("SettingPath", settingPath);
                SystemOpreator.Set("TargetPath", targetPath);

                // 执行数据插件
                foreach (var plug in module.DataPlug)
                {
                    bool found = false;
                    var assemblies = AppDomain.CurrentDomain.GetAssemblies();
                    foreach (var assembly in assemblies)
                    {
                        var types = assembly.GetTypes();
                        foreach (var type in types)
                            if (type.FullName == plug.Value)
                            {
                                found = true;
                                object? obj = Activator.CreateInstance(type);
                                if (obj is null) { Output?.WriteLine($"[Error] 插件'{plug.Key}/{plug.Value}'初始化失败"); return; }
                                IDataPlug dataPlug = (IDataPlug)obj;
                                dataPlug.Execute(module, plug.Key);
                            };
                    }
                    if (!found) { Output?.WriteLine($"[Error] 未找到插件'{plug.Key}/{plug.Value}'"); return; }
                }

                // 执行生成
                MakeCodeFromConfig(coder, module, sourcePath, targetPath);
            }

        }

        public static void Make(List<CoderConfig> coders, string sourcePath, string targetPath)
        {
            // 读取总的配置文件
            string templetePath = System.IO.Path.Combine(sourcePath, "templete.json");
            string templeteContent = egg.IO.ReadUtf8FileContent(templetePath);
            TempleteConfig templeteConfig = new TempleteConfig();
            if (!templeteContent.IsEmpty()) JsonSerializer.Deserialize<TempleteConfig>(templeteContent);
            foreach (var coder in coders)
            {
                if (!templeteConfig.ContainsKey(coder.NameSpace))
                {
                    TempleteGroup templeteGroup = new TempleteGroup()
                    {
                        EntityPath = coder.NameSpace.Replace('.', '/'),
                        ModulePath = coder.NameSpace.Replace('.', '/'),
                    };
                    templeteConfig.Add(coder.NameSpace, templeteGroup);
                    templeteContent = JsonSerializer.Serialize(templeteConfig);
                    egg.IO.WriteUtf8FileContent(templetePath, templeteContent);
                }
                var group = templeteConfig[coder.NameSpace];
                switch (coder.Type)
                {
                    case CoderType.Module:
                        MakeCodeFromPath(
                            coder,
                            sourcePath,
                            //System.IO.Path.Combine(sourcePath, "modules/" + group.ModulePath + ".json"),
                            System.IO.Path.Combine(sourcePath, "configs"),
                            System.IO.Path.Combine(sourcePath, "codes"),
                            targetPath);
                        break;
                }
            }
        }
    }
}
